package drds.plus.sql_process.abstract_syntax_tree.node.query;

import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.ExecutePlan;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.JoinStrategy;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.BooleanFilter;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.plus.sql_process.abstract_syntax_tree.node.Node;
import drds.plus.sql_process.abstract_syntax_tree.node.query.build.JoinBuilder;
import drds.plus.sql_process.abstract_syntax_tree.node.query.build.QueryBuilder;
import drds.plus.sql_process.utils.Filters;
import drds.plus.sql_process.utils.OptimizerUtils;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Join extends Query {
    @Setter
    @Getter
    private JoinBuilder joinQueryNodeBuilder;
    @Setter
    @Getter
    private boolean leftOuterJoin = false;
    @Setter
    @Getter
    private boolean rightOuterJoin = false;
    @Setter
    @Getter
    private List<BooleanFilter> joinFilterList = new ArrayList<BooleanFilter>();
    @Setter
    @Getter
    private boolean needOptimizeJoinOrder = true;
    @Setter
    @Getter
    private JoinStrategy joinStrategy = JoinStrategy.nest_loop_join;
    //
    /**
     * 主键join
     */
    @Setter
    @Getter
    private boolean primaryKeyJoin = false;

    public Join() {
        super();
        joinQueryNodeBuilder = new JoinBuilder(this);
    }

    public List<Item> getLeftJoinItemList() {
        List<Item> itemList = new ArrayList<Item>(this.getJoinFilterList().size());
        for (BooleanFilter booleanFilter : this.getJoinFilterList()) {
            itemList.add((Item) booleanFilter.getColumn());
        }
        return itemList;
    }

    public List<Item> getRightJoinItemList() {
        List<Item> itemList = new ArrayList<Item>(this.getJoinFilterList().size());
        for (BooleanFilter booleanFilter : this.getJoinFilterList()) {
            itemList.add((Item) booleanFilter.getValue());
        }
        return itemList;
    }

    public void addJoinKeys(Item leftKey, Item rightKey) {
        this.joinFilterList.add(Filters.equal(leftKey, rightKey));
        setNeedBuild(true);

    }

    public void addJoinKeys(String leftKey, String rightKey) {
        this.addJoinKeys(OptimizerUtils.createColumnFromString(leftKey), OptimizerUtils.createColumnFromString(rightKey));
    }

    public void addJoinFilter(BooleanFilter joinFilter) {
        this.joinFilterList.add(joinFilter);
        setNeedBuild(true);
    }

    public Query getLeftNode() {
        if (this.getNodeList() == null || this.getNodeList().isEmpty()) {
            return null;
        }
        return (Query) this.getNodeList().get(0);
    }

    public void setLeftNode(Query leftNode) {
        if (this.getNodeList().isEmpty()) {
            this.getNodeList().add(leftNode);
        } else {
            this.getNodeList().set(0, leftNode);
        }
        setNeedBuild(true);
    }

    public Query getRightNode() {
        if (this.getNodeList() == null || this.getNodeList().size() < 2) {
            return null;
        }

        return (Query) this.getNodeList().get(1);
    }

    public void setRightNode(Query rightNode) {
        if (this.getNodeList().isEmpty()) {
            this.getNodeList().add(null);
        }
        if (this.getNodeList().size() == 1) {
            this.getNodeList().add(rightNode);
        } else {
            this.getNodeList().set(1, rightNode);
        }

        setNeedBuild(true);
    }

    public List<Node> getNodeList() {
        List<Node> nodeList = super.getNodeList();
        nodeList.remove(null);// 删除left为null的情况
        return nodeList;
    }

    public List<OrderBy> getCompleteOrderByList() {
        List<OrderBy> orderByListCombinedWithGroupBy = getOrderByListCombinedWithGroupByList();
        if (orderByListCombinedWithGroupBy != null) {
            return orderByListCombinedWithGroupBy;
        }

        List<OrderBy> orderByList = new ArrayList<OrderBy>();
        // index nested loop以左表顺序为准
        if (this.getJoinStrategy() == JoinStrategy.index_nest_loop_join || this.getJoinStrategy() == JoinStrategy.nest_loop_join) {
            // 去掉隐式的主键列排序,会产生额外的开销,比如会优化为concurrent模式,占用过多链接
            // orders = this.getLeftNode().getCompleteOrderByList();
        } else if (this.getJoinStrategy() == JoinStrategy.sort_merge_join) {
            // sort merge的话，返回空值，由上层来判断
        }
        /**
         * 仅支持order by前缀匹配
         */
        List<OrderBy> orderByList1 = orderByList;
        List<OrderBy> implicitOrderByList = new ArrayList();
        for (int i = 0; i < orderByList1.size(); i++) {
            if (this.getSelectItemList().contains(orderByList1.get(i).getItem())) {
                implicitOrderByList.add(orderByList1.get(i));
            } else {
                break;
            }
        }

        return implicitOrderByList;
    }

    public QueryBuilder getQueryNodeBuilder() {
        return joinQueryNodeBuilder;
    }

    public String getName() {
        return this.getAlias();
    }

    public void build() {
        if (this.isNeedBuild()) {
            this.getLeftNode().build();
            this.getRightNode().build();
            this.joinQueryNodeBuilder.build();
        }

        setNeedBuild(false);
    }

    /**
     * 交换左右节点
     */
    public void exchangeLeftAndRight() {
        setNeedBuild(true);

        Query query = this.getLeftNode();
        this.setLeftNode(this.getRightNode());
        this.setRightNode(query);

        boolean leftOuter = this.leftOuterJoin;
        this.leftOuterJoin = this.rightOuterJoin;
        this.rightOuterJoin = leftOuter;

    }

    public ExecutePlan toExecutePlan(int shareIndex) {
        subQueryToExecutePlan(shareIndex);
        drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join join = ObjectCreateFactory.createJoin();
        // 不能传递shareIndex,代理对象会自处理
        join.setRightNode((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) this.getRightNode().toExecutePlan());
        join.setLeftNode((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) this.getLeftNode().toExecutePlan());
        join.setJoinStrategy(this.getJoinStrategy());
        join.setLeftOuterJoin(this.getLeftOuterJoin());
        join.setRightOuterJoin(this.getRightOuterJoin());
        join.setJoinItemList((this.getLeftJoinItemList()), (this.getRightJoinItemList()));
        join.setOrderByList(this.getOrderByList());
        join.setLimitFrom(this.getLimitFrom());
        join.setLimitTo(this.getLimitTo());
        join.setConsistent(true);
        join.setValueFilter(this.getResultFilter());
        join.having(this.getHaving());
        join.setAlias(this.getAlias());
        join.setGroupByList(this.getGroupByList());
        join.setIsSubQuery(this.isSubQuery());
        join.setOtherJoinOnFilter(this.getOtherJoinOnFilter());
        join.setSelectItemList((this.getSelectItemList()));
        join.setWhereFilter(this.getAllWhereFilter());
        join.setExistAggregate(this.isExistAggregateExpression());
        join.setDataNodeId(this.getDataNodeId(shareIndex));
        join.setSubQueryFilterId(this.getSubQueryId());
        join.setSubQueryFilter(this.getSubQueryFunctionFilter());
        join.setCorrelatedSubQuery(this.isCorrelatedSubQuery());
        join.setExistSequenceValue(this.isExistSequenceValue());
        return join;
    }

    /**
     * 进行INDEX_NEST_LOOP模式下join列没有join列的情况进行a join (b join c)==(a jon b) join c
     */
    public Query convertToJoinIfNeed() {
        super.convertToJoinIfNeed(); // 首先执行一次TableNode处理，生成join

        // 如果右边是子查询，join策略为block，不做调整
        if (!(this.getJoinStrategy() == JoinStrategy.index_nest_loop_join)) {
            return this;
        }
        Query rightNode = this.getRightNode();
        // 如果右边是一个IQuery，就按正常的方法生成JoinNode即可
        if (rightNode instanceof TableQuery || rightNode instanceof $Query$ || rightNode instanceof MergeQuery) {
            return this;
        }

        assert (rightNode instanceof Join);// 右边也是一个join
        // 将原本 A join (B JoinImpl C) 调整为 (A join B) join C
        // 原本B join C可能是TableNode中走的索引信息不包含字段信息，需要做回表查询
        Query leftNode = this.getLeftNode();
        if (rightNode instanceof Join) {
            Query rightNode$LeftNode = ((Join) rightNode).getLeftNode();
            Query rightNode$RightNode = ((Join) rightNode).getRightNode();
            //
            Join leftNodeJoinRightNode$LeftNode = leftNode.join(rightNode$LeftNode);
            leftNodeJoinRightNode$LeftNode.setJoinStrategy(JoinStrategy.index_nest_loop_join);
            //join条件
            // 复制join的右字段，修正一下表名
            List<Item> rightNode$LeftNodeJoinItemList = OptimizerUtils.copySelectItemList(this.getRightJoinItemList(), null, rightNode$LeftNode.getName());
            for (int i = 0; i < this.getLeftJoinItemList().size(); i++) {
                leftNodeJoinRightNode$LeftNode.addJoinKeys(this.getLeftJoinItemList().get(i), rightNode$LeftNodeJoinItemList.get(i));
            }
            //查询项
            List<Item> leftNodeSelectItemList = OptimizerUtils.copySelectItemList(leftNode.getSelectItemList(), null, leftNode.getName());
            List<Item> rightNode$LeftNodeSelectItemList = OptimizerUtils.copySelectItemList(rightNode$LeftNode.getSelectItemList(), null, rightNode$LeftNode.getName());
            List<Item> selectItemList = new LinkedList<Item>();
            selectItemList.addAll(leftNodeSelectItemList);
            selectItemList.addAll(rightNode$LeftNodeSelectItemList);
            // leftNode + index的查询做为新的join查询字段
            leftNodeJoinRightNode$LeftNode.setSelectItemListAndSetNeedBuild(selectItemList);
            //
            leftNodeJoinRightNode$LeftNode.setLeftRightJoin(this.leftOuterJoin, this.rightOuterJoin);
            leftNodeJoinRightNode$LeftNode.setParent(this.getParent());
            leftNodeJoinRightNode$LeftNode.setDataNodeId(this.getDataNodeId());
            //
            // (leftNode join index) join key构建
            Join leftNodeJoinRightNode$LeftNode$$rightNode$RightNode = leftNodeJoinRightNode$LeftNode.join(rightNode$RightNode);
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setJoinStrategy(JoinStrategy.index_nest_loop_join); // 也是走index
            List<Item> rightNode$LeftNodeJoinItemList2 = OptimizerUtils.copySelectItemList(((Join) rightNode).getLeftJoinItemList(), null, rightNode$LeftNode.getName());
            for (int i = 0; i < rightNode$LeftNodeJoinItemList2.size(); i++) {
                leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.addJoinKeys(rightNode$LeftNodeJoinItemList2.get(i), ((Join) rightNode).getRightJoinItemList().get(i));
            }
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setLeftRightJoin(this.leftOuterJoin, this.rightOuterJoin);
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setOrderByListAndSetNeedBuild(this.getOrderByList());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setConsistent(true);
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setLimitFrom(this.getLimitFrom());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setLimitTo(this.getLimitTo());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setAlias(this.getAlias());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setTableSubAliasAndSetNeedBuild(this.getSubAlias());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setDataNodeId(this.getDataNodeId());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setSelectItemList(this.getSelectItemList());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setGroupByListAndSetNeedBuild(this.getGroupByList());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setResultFilterAndSetNeedBuild(this.getResultFilter());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setOtherJoinOnFilter(this.getOtherJoinOnFilter());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setSubQueryAndSetNeedBuild(this.isSubQuery());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setSubQueryFunctionFilter(this.getSubQueryFunctionFilter());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setAllWhereFilter(this.getAllWhereFilter());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setLockMode(this.getLockMode());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setParent(this.getParent());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.setCorrelatedSubQuery(this.isCorrelatedSubQuery());
            leftNodeJoinRightNode$LeftNode$$rightNode$RightNode.build();
            return leftNodeJoinRightNode$LeftNode$$rightNode$RightNode;
        }

        return this;
    }


    public void setLeftOuterJoin() {
        this.leftOuterJoin = true;

    }

    public void setRightOuterJoin() {
        this.rightOuterJoin = true;

    }

    public void setInnerJoin() {
        this.leftOuterJoin = false;
        this.rightOuterJoin = false;

    }

    /**
     * 或者称为full join
     */
    public void setFullOuterJoin() {
        this.leftOuterJoin = true;
        this.rightOuterJoin = true;

    }

    public boolean getLeftOuterJoin() {
        return this.leftOuterJoin;
    }

    public boolean getRightOuterJoin() {
        return this.rightOuterJoin;
    }

    public boolean isLeftOuterJoin() {
        return (this.getLeftOuterJoin()) && (!this.getRightOuterJoin());
    }

    public boolean isRightOuterJoin() {
        return (!this.getLeftOuterJoin()) && (this.getRightOuterJoin());
    }

    public boolean isInnerJoin() {
        return (!this.getLeftOuterJoin()) && (!this.getRightOuterJoin());
    }

    public boolean isFullOuterJoin() {
        return (this.getLeftOuterJoin()) && (this.getRightOuterJoin());
    }


    public void setLeftRightJoin(boolean leftOuter, boolean rightOuter) {
        this.leftOuterJoin = leftOuter;
        this.rightOuterJoin = rightOuter;

    }

    public Join copy() {
        Join joinNode = new Join();
        this.copySelfTo(joinNode);
        joinNode.setJoinFilterList(new ArrayList<BooleanFilter>(this.getJoinFilterList()));
        joinNode.setJoinStrategy(this.getJoinStrategy());
        joinNode.setLeftNode(this.getLeftNode().copy());
        joinNode.setRightNode(this.getRightNode().copy());
        joinNode.setNeedOptimizeJoinOrder(this.isNeedOptimizeJoinOrder());

        joinNode.leftOuterJoin = this.leftOuterJoin;
        joinNode.rightOuterJoin = this.rightOuterJoin;
        joinNode.primaryKeyJoin = this.primaryKeyJoin;
        return joinNode;
    }

    public Join copySelf() {
        Join joinNode = new Join();
        this.copySelfTo(joinNode);
        joinNode.setJoinFilterList(new ArrayList<BooleanFilter>(this.getJoinFilterList()));
        joinNode.setJoinStrategy(this.getJoinStrategy());
        joinNode.setLeftNode(this.getLeftNode());
        joinNode.setRightNode(this.getRightNode());
        joinNode.setNeedOptimizeJoinOrder(this.isNeedOptimizeJoinOrder());

        joinNode.leftOuterJoin = this.leftOuterJoin;
        joinNode.rightOuterJoin = this.rightOuterJoin;
        joinNode.primaryKeyJoin = this.primaryKeyJoin;
        return joinNode;
    }

    public Join deepCopy() {
        Join joinNode = new Join();
        this.deepCopySelfTo(joinNode);
        joinNode.setJoinFilterList(OptimizerUtils.copyFilter(this.getJoinFilterList()));
        joinNode.setJoinStrategy(this.getJoinStrategy());
        joinNode.setLeftNode(this.getLeftNode().deepCopy());
        joinNode.setRightNode(this.getRightNode().deepCopy());
        joinNode.setNeedOptimizeJoinOrder(this.isNeedOptimizeJoinOrder());

        joinNode.leftOuterJoin = this.leftOuterJoin;
        joinNode.rightOuterJoin = this.rightOuterJoin;
        joinNode.primaryKeyJoin = this.primaryKeyJoin;
        return joinNode;
    }
}
